// Implementation of the Socket class.


#include "Socket.h"
#include "string.h"
#include <string>
#include <iostream>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include "../Constants.h"

using namespace std;

Socket::Socket() :
  m_sock ( -1 )
{

  memset ( &m_addr,
	   0,
	   sizeof ( m_addr ) );

}

Socket::~Socket()
{
  if ( is_valid() )
    ::close ( m_sock );
}

bool Socket::create( )
{
  m_sock = socket ( AF_INET,
		    SOCK_STREAM,
		    0 );

  if ( ! is_valid() )
    return false;


  // TIME_WAIT - argh
  int on = 1;
  if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
  return false;

  /*if(timeout>-1){
    struct timeval tv;
    tv.tv_sec=timeout;
    tv.tv_usec = 0;
    if(setsockopt(m_sock,SOL_SOCKET,SO_RCVTIMEO, (char*)&tv,sizeof(tv))){
      perror("setsockopt sdlkf");
      return false;
    }
    }*/


  return true;

}

bool Socket::bind ( const int port )
{

  if ( ! is_valid() )
    {
      return false;
    }

  m_addr.sin_family = AF_INET;
  m_addr.sin_addr.s_addr = INADDR_ANY;
  m_addr.sin_port = htons ( port );

  int bind_return = ::bind ( m_sock,
			     ( struct sockaddr * ) &m_addr,
			     sizeof ( m_addr ) );

  if ( bind_return == -1 )
    {
      return false;
    }

  return true;
}


bool Socket::listen() const
{
  if ( ! is_valid() )
    {
      return false;
    }

  int listen_return = ::listen ( m_sock, MAXCONNECTIONS );


  if ( listen_return == -1 )
    {
      return false;
    }

  return true;
}


bool Socket::accept ( Socket& new_socket ) const
{
  int addr_length = sizeof ( m_addr );
  new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );

  if ( new_socket.m_sock <= 0 )
    return false;
  else
    return true;
}


bool Socket::sendfile ( const std::string s) const
{
  ifstream snd_file;
  char buffer[MAXRECV];

  snd_file.open(s.c_str());
  unsigned long int end;
  snd_file.seekg(0,ios::end);
  end=snd_file.tellg();
  snd_file.close();
  
  cout << s.c_str() << endl << s.size() << endl;

  char buf[15];
  sprintf(buffer,"%lu",end);
  ::send(m_sock,buffer,8,MSG_NOSIGNAL);
  ::recv(m_sock,buf,15,0);
  ::send(m_sock,s.c_str(),s.size(),MSG_NOSIGNAL);
  ::recv(m_sock,buf,15,0);
  
  string temp;
  string command;
  command=FTPPY+" put "+s;

  //FORK!
  pid_t pid = vfork();
  if(pid==0)//child!
    {
      printf("I AM SEND CHILD\n");
      system(command.c_str());
      printf("I AM SEND CHILD2\n");
      _exit(0);
    }
  printf("I AM SEND PARENT\n");

  //Notify recv side the file is on the tcp server
  ::send(m_sock,s.c_str(),s.size(),MSG_NOSIGNAL);

  //Wait for server side to complete its operation.
  printf("send1\n");
  ::recv(m_sock,buf,15,0);
  printf("send2\n");
  ::send(m_sock,s.c_str(),s.size(),MSG_NOSIGNAL);
  return true;
}

bool Socket::send ( const std::string s ) const
{
  int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL );
  if ( status == -1 )
    {
      return false;
    }
  else
    {
      return true;
    }
}

string Socket::recvfile() const
{
  string fname;
  ofstream rcv_file;

  char buffer[MAXRECV],buf[15];
  int status;
  buf[0]='s';
  unsigned long int length;
  status=::recv(m_sock,buffer,MAXRECV,0);
  length=strtol(buffer,NULL,10);
  memset(buffer,0,MAXRECV);
  ::send(m_sock,buf,15,MSG_NOSIGNAL);
  status=::recv(m_sock,buffer,MAXRECV,0);
  fname=buffer;
  cout << "fname:" << fname;
  ::send(m_sock,buf,15,MSG_NOSIGNAL);

  rcv_file.open(fname.c_str());

  //Wait for upload to ftp server
  memset(buffer,0,MAXRECV);
  ::recv(m_sock,buffer,MAXRECV,0);

  string temp;
  string command;

  command=FTPPY+" get "+fname;

  //FORK!
  pid_t pid = vfork();
  if(pid==0)//child!
    {
      printf("I AM RECV CHILD\n");
      system(command.c_str());
      printf("I AM RECV CHILD2\n");
      _exit(0);
    }
  printf("I AM RECV PARENT\n");
  
  command=FTPPY+" del "+fname;
  system(command.c_str());

  printf("recv1\n");
  ::send(m_sock,buf,15,MSG_NOSIGNAL);
  printf("recv2\n");
  ::recv(m_sock,buffer,MAXRECV,0);
  return fname;
}

int Socket::recv ( std::string& s ) const
{
  char buf [ MAXRECV + 1];

  s = "";

  memset ( buf, 0, MAXRECV + 1 );

  int status = ::recv ( m_sock, buf, MAXRECV, 0 );

  if ( status == -1 )
    {
      //cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
      return -1;
    }
  else if ( status == 0 )
    {
      return 0;
    }
  else
    {
      s = buf;
      return status;
    }
}

bool Socket::connect ( const std::string host, const int port )
{
  if ( ! is_valid() ) return false;

  m_addr.sin_family = AF_INET;
  m_addr.sin_port = htons ( port );

  int status = inet_pton ( AF_INET, host.c_str(), &m_addr.sin_addr );

  if ( errno == EAFNOSUPPORT ) return false;

  status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) );

  if ( status == 0 )
    return true;
  else
    return false;
}

void Socket::set_non_blocking ( const bool b )
{

  int opts;

  opts = fcntl ( m_sock,
		 F_GETFL );

  if ( opts < 0 )
    {
      return;
    }

  if ( b )
    opts = ( opts | O_NONBLOCK );
  else
    opts = ( opts & ~O_NONBLOCK );

  fcntl ( m_sock,
	  F_SETFL,opts );

}
  
void Socket::close(){
   ::close ( m_sock );
}

string Socket::get_IP(){
  struct sockaddr_in m_addr;
  socklen_t len;

  len = sizeof(m_addr);
  getpeername(m_sock,(struct sockaddr*)&m_addr,&len);
  return (string)inet_ntoa(m_addr.sin_addr);
}
